﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DiracTCC.Business;
using System.Windows.Forms.DataVisualization.Charting;
using System.IO;
using System.Text.RegularExpressions;

namespace DiracTCC.WinForm
{
    public partial class Form1 : Form
    {
        private string pathToXml = string.Empty;
        private XmlAnalyser xmlData;
        private FrameInfo frame;
        //private bool processImages;
        private string originalYuvPath;
        private string formatPathToOriginal;
        private string formatPathToCoded;
        ImageViewer detailsPictureOriginal;
        ImageViewer detailsPictureCoded;
        HtmlViewer detailsSubBlockGrid;

        // instrumentation
        bool renewInstBmpPaths = true;
        string instPredBmp;
        string instMvPosBmp; 
        string instMvPrevBmp; 
        string instSadPosBmp; 
        string instSadPrevBmp;
        string instSplitBmp;

        ImageViewer instPredPictureCoded;
        ImageViewer instMvPosPictureCoded;
        ImageViewer instMvPrevPictureCoded;
        ImageViewer instSadPosPictureCoded;
        ImageViewer instSadPrevPictureCoded;
        ImageViewer instSplitPictureCoded;


        public Form1()
        {
            InitializeComponent();
            toolStripStatusLabel1.Text = "Carregue um arquivo de resultados de codificação";
            timer1.Tick += new EventHandler(timer1_Tick);
        }

        // receive command to execute everything!
        public void ShowResults(string path)
        {
            this.pathToXml = path;
            ProcessSelectedXml();
        }


        // open xml to start everthing
        private void abrirToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                renewInstBmpPaths = true;

                pathToXml = openFileDialog1.FileName;
                ProcessSelectedXml();
            }
        }

        private void ProcessSelectedXml()
        {
            formatPathToOriginal = string.Empty;
            formatPathToCoded = string.Empty;

            originalYuvPath = Path.ChangeExtension(pathToXml, "yuv");
            xmlData = new XmlAnalyser(pathToXml);

            toolStripStatusLabel1.Text = "Carregando arquivo...";
            Application.DoEvents();

            var bmpPath = YuvToBmpConverter.GetBMPPath(originalYuvPath);
            bmpPath = bmpPath.Substring(0, bmpPath.LastIndexOf('\\'));
            if (!Directory.Exists(bmpPath))
            {
                var images = new ProcessImages();
                images.FinishedProcessing += new EventHandler(FirstTimeExecution);

                var video = xmlData.GetVideoInfo();
                images.Show();
                images.StartProcessing(originalYuvPath, video, this);
            }
            else
            {
                LoadFirstTimeData();
            }
        }

        private void codificarUsandoODiracToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var codec = new DiracCodecSettings();
            codec.AssociateCallerForm(this);
            codec.Show();
        }

        // stuf to load data first time
        void FirstTimeExecution(object sender, EventArgs e)
        {
            CallOriginalForm d = new CallOriginalForm(LoadData);
            this.Invoke(d);
        }

        private void LoadFirstTimeData()
        {
            LoadXmlData();
            LoadVideoInfo();
            LoadFrameAllData();

            toolStripStatusLabel1.Text = "Pronto";
            frameTab.Enabled = true;
            Application.DoEvents();
        }

        public delegate void CallOriginalForm();

        public void LoadData()
        {
            // instrumentation
            LoadInstrumentationsVideos(xmlData.GetVideoInfo());
            LoadFirstTimeData();
        }


        // form events
        private void trackBar1_ValueChanged(object sender, EventArgs e)
        {
            scrollPositionTextBox.Text = trackBar1.Value.ToString();

            LoadFrameAllData();
        }

        private void scrollPositionTextBox_TextChanged(object sender, EventArgs e)
        {
            int numericTextBoxValue = 0;

            if (int.TryParse(scrollPositionTextBox.Text, out numericTextBoxValue))
            {
                trackBar1.Value = numericTextBoxValue > trackBar1.Maximum ? trackBar1.Maximum : numericTextBoxValue;
            }
            else
            {
                scrollPositionTextBox.Text = trackBar1.Value.ToString();
            }

            LoadFrameAllData();
        }

        private void scrollPositionTextBox_Click(object sender, EventArgs e)
        {
            scrollPositionTextBox.SelectAll();
        }

        void timer1_Tick(object sender, EventArgs e)
        {
            if (trackBar1.Value + 1 > trackBar1.Maximum)
            {
                trackBar1.Value = trackBar1.Minimum;
            }
            else
            {
                trackBar1.Value++;
            }
        }

        private void frameTab_Selected(object sender, TabControlEventArgs e)
        {
            LoadFrameAllData();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Enabled = !timer1.Enabled;
        }

        private void TimerTextBox_TextChanged(object sender, EventArgs e)
        {
            if (TimerTextBox.Text != null && TimerTextBox.Text != "0" && TimerTextBox.Text != "" && System.Convert.ToDouble(TimerTextBox.Text) < 1000)
                timer1.Interval = System.Convert.ToInt32(1000 / System.Convert.ToDouble(TimerTextBox.Text));
        }


        // executed once, just when the file is opened
        private void LoadVideoInfo()
        {
            VideoInfo video = xmlData.GetVideoInfo();

            // get file sizes
            using (var file = File.Open(Path.ChangeExtension(pathToXml, "yuv"), FileMode.Open))
            {
                originalSizeTextBox.Text = file.Length.ToString("N0");
            }

            using (var file = File.Open(Path.ChangeExtension(pathToXml, "drc"), FileMode.Open))
            {
                codedSizeTextBox.Text = file.Length.ToString("N0");
            }

            PercentageTextBox.Text = Math.Round(100 * (Convert.ToSingle(codedSizeTextBox.Text) / Convert.ToSingle(originalSizeTextBox.Text)), 2).ToString() + " %";

            // generate SAD chart
            GraphHelper.GenerateSadBetweenVideos(
                sadVideosChart, compareSadDataGridView,
                YuvToBmpConverter.GetBMPPath(originalYuvPath),
                YuvToBmpConverter.GetBMPPath(originalYuvPath + ".localdec.yuv"));

            // fill other informations
            chromaTextBox.Text = video.Chroma.ToString();
            videoSizeTextBox.Text = string.Format("{0} x {1}", video.VideoSize[0], video.VideoSize[1]);
            codingInfoTextBox.Text = video.CodingInfo;
            frameRateTextBox.Text = string.Format("{0}/{1}", video.FrameRate[0], video.FrameRate[1]);
            pixelAspectTextBox.Text = string.Format("{0}:{1}", video.PixelAspectRatio[0], video.PixelAspectRatio[1]);
            qfTextBox.Text = video.QF.ToString();
            numL1TextBox.Text = video.NumL1.ToString();
            l1SepTextBox.Text = video.L1Sep.ToString();

            // setup timer

            TimerTextBox.Text = (((float)video.FrameRate[0] / (float)video.FrameRate[1])).ToString();

            timer1.Interval = System.Convert.ToInt32(1000 / System.Convert.ToDouble(TimerTextBox.Text));
        }
        

        // executed ever time the frame is changed
        private void LoadXmlData()
        {
            // set track size
            trackBar1.Maximum = xmlData.GetNumberOfFrames() - 1;
            trackBar1.Minimum = 0;

            // set initial value to textbox
            scrollPositionTextBox.Text = trackBar1.Value.ToString();
        }


        // methods executed dependent upon the selected tab
        private void LoadFrameAllData()
        {
            frame = xmlData.GetFrameInfo(trackBar1.Value);

            if (frameTab.SelectedTab.Name == "frameInfoTabPage")
            {
                LoadFrameInfo(trackBar1.Value);
            }
            else if (frameTab.SelectedTab.Name == "subBlockTabPage")
            {
                LoadSubBlockGrids(trackBar1.Value);
                LoadSubBlockCosts(trackBar1.Value);
                LoadSubBlockData(trackBar1.Value);
                LoadSubBlockCostsData(trackBar1.Value);
            }
            else if (frameTab.SelectedTab.Name == "MVTabPage")
            {
                LoadMVHistogram(trackBar1.Value);
                LoadMVHistogramData(trackBar1.Value);
                LoadMVCost(trackBar1.Value);
            }
            else if (frameTab.SelectedTab.Name == "MVCostTabPage")
            {
                LoadMVCost(trackBar1.Value);
                LoadMVCostData(trackBar1.Value);
            }
            else if (frameTab.SelectedTab.Name == "Instrumentation")
            {
                //ShowInstrumentationsFrames(trackBar1.Value + 1);
            }

            // load independent windows data
            if (detailsSubBlockGrid != null)
            {
                detailsSubBlockGrid.SetHtml(
                    frame, 
                    xmlData.GetSubBlockArray(trackBar1.Value));
            }


            ShowFramePictures(trackBar1.Value + 1);

            if (detailsPictureOriginal != null)
                detailsPictureOriginal.SetImage(framePictureBox.ImageLocation);

            if (detailsPictureCoded != null)
                detailsPictureCoded.SetImage(newFramePictureBox.ImageLocation);


            ShowInstrumentationsFrames(trackBar1.Value + 1);

            if (instPredPictureCoded != null)
                instPredPictureCoded.SetImage(instPredPictureBox.ImageLocation);

            if (instMvPosPictureCoded != null)
                instMvPosPictureCoded.SetImage(instMvPosPictureBox.ImageLocation);

            if (instMvPrevPictureCoded != null)
                instMvPrevPictureCoded.SetImage(instMvPrevPictureBox.ImageLocation);

            if (instSadPosPictureCoded != null)
                instSadPosPictureCoded.SetImage(instSadPosPictureBox.ImageLocation);

            if (instSadPrevPictureCoded != null)
                instSadPrevPictureCoded.SetImage(instSadPrevPictureBox.ImageLocation);

            if (instSplitPictureCoded != null)
                instSplitPictureCoded.SetImage(instSplitPictureBox.ImageLocation);
        }


        // moviment vector data
        private void LoadMVHistogram(int position)
        {
            // Clear Prev data
            if (histogramPrevMVchart.Series.Count(a => a.Name == "histogram") == 0)
            {
                histogramPrevMVchart.Series.Add("histogram");
                histogramPrevMVchart.Series["histogram"].ChartType = SeriesChartType.Column;
            }

            histogramPrevMVchart.Series["histogram"].Points.Clear();

            // Clear Pos data
            if (histogramPosMVchart.Series.Count(a => a.Name == "histogram") == 0)
            {
                histogramPosMVchart.Series.Add("histogram");
                histogramPosMVchart.Series["histogram"].ChartType = SeriesChartType.Column;
            }

            histogramPosMVchart.Series["histogram"].Points.Clear();

            if (frame.Ref1.HasValue)
            {
                var points = xmlData.GetMVVectorsSize(position, 0);

                foreach (var point in points.OrderBy(a => a.x))
                {
                    histogramPrevMVchart.Series["histogram"].Points.Add(new DataPoint { XValue = point.x, YValues = new double[1] { point.y } });
                }
            }
            if (frame.Ref2.HasValue)
            {
                var points = xmlData.GetMVVectorsSize(position, 1);

                foreach (var point in points.OrderBy(a => a.x))
                {
                    histogramPosMVchart.Series["histogram"].Points.Add(new DataPoint { XValue = point.x, YValues = new double[1] { point.y } });
                }
            }

        }

        private void LoadMVHistogramData(int position)
        {
            histogramPrevMVdataGridView.DataSource = null;
            histogramPosMVdataGridView.DataSource = null;

            if (frame.Ref1.HasValue)
            {
                var points = xmlData.GetMVVectorsSize(position, 0);
                DataTable tbl = new DataTable();

                tbl.Columns.Add("valor");
                tbl.Columns.Add("frequencia");


                foreach (var item in points.OrderBy(a => a.x))
                {
                    tbl.Rows.Add(item.x, item.y);
                }

                histogramPrevMVdataGridView.DataSource = tbl;
            }
            if (frame.Ref2.HasValue)
            {
                var points = xmlData.GetMVVectorsSize(position, 1);
                DataTable tbl = new DataTable();

                tbl.Columns.Add("valor");
                tbl.Columns.Add("frequencia");


                foreach (var item in points.OrderBy(a => a.x))
                {
                    tbl.Rows.Add(item.x, item.y);
                }

                histogramPosMVdataGridView.DataSource = tbl;
            }
        }

        private void LoadMVCost(int position)
        {
            GraphHelper.Clear3DChart(costPrevMVchart);
            GraphHelper.Clear3DChart(costPosMVchart);

            if (frame.Ref1.HasValue)
            {
                GraphHelper.Fill3DChart(
                    xmlData.GetMVCosts(position, 0),
                    costPrevMVchart);
            }
            if (frame.Ref2.HasValue)
            {
                GraphHelper.Fill3DChart(
                    xmlData.GetMVCosts(position, 1),
                    costPosMVchart);
            }
        }

        private void LoadMVCostData(int position)
        {
            costMVPrevDataGridView.DataSource = null;
            costMVPosDataGridView.DataSource = null;

            if (frame.Ref1.HasValue)
            {
                GraphHelper.FillDataGrid(
                    xmlData.GetMVCosts(position, 0), 
                    costMVPrevDataGridView);
            }

            if (frame.Ref2.HasValue)
            {
                GraphHelper.FillDataGrid(
                    xmlData.GetMVCosts(position, 1),
                    costMVPosDataGridView);
            }
        }


        // subblock data
        private void LoadSubBlockGrids(int position)
        {
            SubBlockArray array = xmlData.GetSubBlockArray(position);

            subBlockDivisionGrid.DocumentText = 
                GraphHelper.GetSubBlockGrid(frame, array, subBlockDivisionGrid.Width, subBlockDivisionGrid.Height);
        }

        private void LoadSubBlockData(int position)
        {
            int[] lenght = frame.GetSubBlockLenghtArray();
            SubBlockArray array = xmlData.GetSubBlockArray(position);
            DataTable tbl = new DataTable();

            for (int i = 0; i < lenght[0]; i++)
            {
                tbl.Columns.Add(i.ToString());
            }


            for (int i = 0; i < lenght[1]; i++)
            {
                object[] info = new object[tbl.Columns.Count];
                for (int j = 0; j < tbl.Columns.Count; j++)
                {
                    info[j] = array.GetSubBlock(j, i).SplitMode;
                }

                tbl.Rows.Add(info);
            }

            SubBlockModeGridView.DataSource = tbl;
        }

        private void LoadSubBlockCosts(int position)
        {
            GraphHelper.Clear3DChart(SubBlockChart);
            GraphHelper.Fill3DChart(
                    xmlData.GetSubBlockCosts(position),
                    SubBlockChart);
        }

        private void LoadSubBlockCostsData(int position)
        {
            GraphHelper.FillDataGrid(
                    xmlData.GetSubBlockCosts(position),
                    SubBlockCostGridView);
        }


        // frame info
        private void LoadFrameInfo(int framePosition)
        {
            frameNumberTextBox.Text = frame.FrameNumber.ToString();
            frameTypeTextBox.Text = frame.PictureType;
            subBlockTextBox.Text = frame.SubBlockLenght;
            mVTextBox.Text = frame.MVLenght;
            blockSeptextBox.Text = frame.BlockSeparation;
            referencesTextBox.Text = frame.References;

            if (string.IsNullOrEmpty(formatPathToOriginal))
            {
                formatPathToOriginal = YuvToBmpConverter.GetBMPPath(originalYuvPath);
                formatPathToCoded = YuvToBmpConverter.GetBMPPath(originalYuvPath + ".localdec.yuv");
            }

            //Anterior: 0, Posterior: 3
            if (frame.Ref1.HasValue)
            {
                string num = Regex.Match(frame.References, "Ref1: (?<num>[0-9]+)").Groups[1].Value;
                prevPictureBox.ImageLocation = string.Format(formatPathToOriginal, int.Parse(num.Trim()) + 1);
                newPrevPictureBox.ImageLocation = string.Format(formatPathToCoded, int.Parse(num.Trim()) + 1);
            }
            if (frame.Ref2.HasValue)
            {
                string num = Regex.Match(frame.References, "Ref2: (?<num>[0-9]+)").Groups[1].Value;
                nxtPictureBox.ImageLocation = string.Format(formatPathToOriginal, int.Parse(num.Trim()) + 1);
                newNxtPictureBox.ImageLocation = string.Format(formatPathToCoded, int.Parse(num.Trim()) + 1);
            }
        }

        private void ShowFramePictures(int framePosition)
        {
            framePictureBox.ImageLocation = string.Format(formatPathToOriginal, framePosition);
            newFramePictureBox.ImageLocation = string.Format(formatPathToCoded, framePosition);
        }

        private void framePictureBox_Click(object sender, EventArgs e)
        {
            detailsPictureOriginal = new ImageViewer();
            detailsPictureOriginal.SetImage(framePictureBox.ImageLocation);
            detailsPictureOriginal.Show();
        }

        private void newFramePictureBox_Click(object sender, EventArgs e)
        {
            detailsPictureCoded = new ImageViewer();
            detailsPictureCoded.SetImage(newFramePictureBox.ImageLocation);
            detailsPictureCoded.Show();
        }

        private void label27_Click(object sender, EventArgs e)
        {
            detailsSubBlockGrid = new HtmlViewer();

            SubBlockArray array = xmlData.GetSubBlockArray(trackBar1.Value);
            detailsSubBlockGrid.SetHtml(frame, array);

            detailsSubBlockGrid.Show();
        }


        // instrumentation
        private void ShowInstrumentationsFrames(int position)
        {
            if (renewInstBmpPaths)
            {
                instPredBmp = 
                    YuvToBmpConverter.GetBMPPath(
                    InstrumentationGenerator.GetInstrumentationPath(
                        originalYuvPath, "-pred_mode"));

                instMvPosBmp =
                    YuvToBmpConverter.GetBMPPath(
                    InstrumentationGenerator.GetInstrumentationPath(
                        originalYuvPath, "-ref 2 -motion_colour_arrows"));

                instMvPrevBmp =
                    YuvToBmpConverter.GetBMPPath(
                    InstrumentationGenerator.GetInstrumentationPath(
                        originalYuvPath, "-motion_colour_arrows"));

                instSadPosBmp =
                    YuvToBmpConverter.GetBMPPath(
                    InstrumentationGenerator.GetInstrumentationPath(
                        originalYuvPath, "-ref 2 -sad"));

                instSadPrevBmp =
                    YuvToBmpConverter.GetBMPPath(
                    InstrumentationGenerator.GetInstrumentationPath(
                        originalYuvPath, "-sad"));

                instSplitBmp =
                    YuvToBmpConverter.GetBMPPath(
                    InstrumentationGenerator.GetInstrumentationPath(
                        originalYuvPath, "-split_mode"));

            }

            instPredPictureBox.ImageLocation = string.Format(instPredBmp, position);
            instMvPosPictureBox.ImageLocation = string.Format(instMvPosBmp, position);
            instMvPrevPictureBox.ImageLocation = string.Format(instMvPrevBmp, position);
            instSadPosPictureBox.ImageLocation = string.Format(instSadPosBmp, position);
            instSadPrevPictureBox.ImageLocation = string.Format(instSadPrevBmp, position);
            instSplitPictureBox.ImageLocation = string.Format(instSplitBmp, position);
        }

        private void LoadInstrumentationsVideos(VideoInfo instVideo)
        {
            var instPath = Path.ChangeExtension(pathToXml, "yuv");
            string[] args = { "-motion_colour_arrows", "-ref 2 -motion_colour_arrows",
                            "-split_mode", "-pred_mode", "-sad", "-ref 2 -sad"};

            ProcessImages p1 = new ProcessImages();
            p1.Show();
            p1.StartProcessing(instPath, instVideo, args, this);
        }

        private void instPredPictureBox_Click(object sender, EventArgs e)
        {
            instPredPictureCoded = new ImageViewer();
            instPredPictureCoded.SetImage(instPredPictureBox.ImageLocation);
            instPredPictureCoded.Show();
        }

        private void instSplitPictureBox_Click(object sender, EventArgs e)
        {
            instSplitPictureCoded = new ImageViewer();
            instSplitPictureCoded.SetImage(instSplitPictureBox.ImageLocation);
            instSplitPictureCoded.Show();
        }

        private void instMvPrevPictureBox_Click(object sender, EventArgs e)
        {
            instMvPrevPictureCoded = new ImageViewer();
            instMvPrevPictureCoded.SetImage(instMvPrevPictureBox.ImageLocation);
            instMvPrevPictureCoded.Show();
        }

        private void instMvPosPictureBox_Click(object sender, EventArgs e)
        {
            instMvPosPictureCoded = new ImageViewer();
            instMvPosPictureCoded.SetImage(instMvPosPictureBox.ImageLocation);
            instMvPosPictureCoded.Show();
        }

        private void instSadPrevPictureBox_Click(object sender, EventArgs e)
        {
            instSadPrevPictureCoded = new ImageViewer();
            instSadPrevPictureCoded.SetImage(instSadPrevPictureBox.ImageLocation);
            instSadPrevPictureCoded.Show();
        }

        private void instSadPosPictureBox_Click(object sender, EventArgs e)
        {
            instSadPosPictureCoded = new ImageViewer();
            instSadPosPictureCoded.SetImage(instSadPosPictureBox.ImageLocation);
            instSadPosPictureCoded.Show();
        }
    }
}
